#ifndef __zluaobj_h__
#define __zluaobj_h__
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
#include "zlogger.h"
#include "zvar.h"
#include "zutilinc.h"

ZUTIL_API int zLoadFile(const char* dir, const char* file,zString& context);
typedef int ZLUAIDX;
class ZUTIL_API zLuaObj
{
public:
	class STACKKEEPER
	{
		lua_State* m_L;
		int m_pos;
	public:
		STACKKEEPER(lua_State *L)
			:m_L(L)
		{
			m_pos=lua_gettop(L);
			if (m_pos>10)
				zlogger_warning(NULL,"lua stack %p=%d\n",L,m_pos);
		}
		~STACKKEEPER(){lua_settop(m_L,m_pos);}
		int getsavedpos(){return m_pos;}
	};
protected:
	int m_ref;
public:
	lua_State* m_L;
public:
	~zLuaObj();
	zLuaObj():m_L(NULL){};
	const zLuaObj& operator = (const zLuaObj& b)
	{
		linkTo(b);
		return *this;
	}
	zLuaObj(const zLuaObj& org)
		:m_L(NULL)
	{
		linkTo(org);
	}
	zLuaObj(lua_State* L);
	zLuaObj(lua_State* L,const bool eattop);
	void init(lua_State* L);
	zLuaObj(lua_State* L,const char* global);//Creates a reference to an object in the variable globalName
	zLuaObj(lua_State* L,int idx);//Creates a reference to an object in the given index of the stack
	zLuaObj(const zLuaObj& parent, const char* name);//Creates a reference to an object inside another object
	zLuaObj(const zLuaObj* pparent, const char* name);//Creates a reference to an object inside another object
	zLuaObj(const zLuaObj& parent, int name); //creates a Object from a table that is indexed by a number.
	zLuaObj(const zLuaObj& parent, const zLuaObj& name);//creates a Object from a table that is indexed by a Object
	void linkTo(lua_State* L,const char* global);
	void linkTo(lua_State* L,int idx);
	void linkTo(const zLuaObj& org);
	void linkTo(const zLuaObj& parent, const char* name);
	void SetNil();
	void setString(const char* val);
	void setTable(int inum=0,int hnum=0);
	void registerValue(int index)
	{
		lua_pushvalue(m_L,index);
		m_ref = luaL_ref(m_L,LUA_REGISTRYINDEX);
	}
	void push()const
	{
		lua_rawgeti(m_L,LUA_REGISTRYINDEX,m_ref);
	}
public:
	int getRef()const {return m_ref;};
	bool isluafalse()const 
	{
		push();
		bool v = lua_isnil(m_L,-1)||(lua_isboolean(m_L,-1)&&!lua_toboolean(m_L,-1));
		lua_pop(m_L,1);
		return v;
	}
	bool isNil()const 
	{
		push();
		bool v = lua_isnil(m_L,-1);
		lua_pop(m_L,1);
		return v;
	}
	bool isBool()const 
	{
		push();
		bool v = lua_isboolean(m_L,-1);
		lua_pop(m_L,1);
		return v;
	}
	bool isNumber()const 
	{
		push();
		int v = lua_isnumber(m_L,-1);
		lua_pop(m_L,1);
		return v!=0;
	}
	bool isString()const 
	{
		push();
		int v = lua_isstring(m_L,-1);
		lua_pop(m_L,1);
		return v!=0;
	}
	bool isFunction()const 
	{
		push();
		bool v = lua_isfunction(m_L,-1);
		lua_pop(m_L,1);
		return v;
	}
	bool isTable()const 
	{
		push();
		bool v = lua_istable(m_L,-1);
		lua_pop(m_L,1);
		return v;
	}
	bool isUserdata()const 
	{
		push();
		int v = lua_isuserdata(m_L,-1);
		lua_pop(m_L,1);
		return v!=0;
	}
	bool isThread()const 
	{
		push();
		int v = lua_isthread(m_L,-1);
		lua_pop(m_L,1);
		return v!=0;
	}
	int type()const 
	{
		push();
		int type =lua_type(m_L,-1);
		lua_pop(m_L,1);
		return type;
	}
	lua_State *getThread()
	{
		push();
		lua_State * v = lua_tothread(m_L,-1);
		lua_pop(m_L,1);
		return v;
	}
	bool getBoolean()const 
	{
		push();
		int v = lua_toboolean(m_L,-1);
		lua_pop(m_L,1);
		return v!=0;
	}
	double getNumber()const 
	{
		push();
		double v = lua_tonumber(m_L,-1);
		lua_pop(m_L,1);
		return v;
	}
	const char* getString()const 
	{
		push();
		const char* v = lua_tostring(m_L,-1);
		lua_pop(m_L,1);
		return v;
	}
	zLuaObj getField(const char* name)const 
	{
		return zLuaObj(*this,name);
	}
	int setFieldS(const char* name,const char* val);
	int call_va(int (*ptn)(struct lua_State *L),const char *sig, ...);
	int call_va_err (zString* err, int (*ptn)(struct lua_State *L), const char *sig, ...);
	int call_err(zString* err, int (*ptn)(struct lua_State *L), const char *sig, va_list l);
	int call_rva(int tracebackref,const char *sig, ...);
	int call_rva_err (zString* err, int tracebackref, const char *sig, ...);
	int call_rerr(zString* err, int tracebackref, const char *sig, va_list l);
	int call_rp_err_ (zString* err,int tracebackref, int (*ptb)(struct lua_State *L),const char *sig, va_list vl);
};

class ZLuaEnvDelegate;
class ZUTIL_API ZLuaEnv
{
public:
	enum EPATH{
		ZPATH_DEFAULT=0,
		ZPATH_ROOT=1,
		ZPATH_MOD=2,
		ZPATH_APP=3,
		ZPATH_DOC=4,
		ZPATH_CACHE=5,
		ZPATH_TMP=6,
		ZPATH_MAXNUM
	};
	ZLuaEnv(ZLuaEnvDelegate* pd);
	ZLuaEnv(ZLuaEnvDelegate* pd, lua_State* L);
	~ZLuaEnv();
	inline operator lua_State* () const { return m_L; }
public:
	const zString& GetPath(EPATH p=ZPATH_DEFAULT);
	int loadAT(const char* filename,zVar& ov,zVar* pin,EPATH p=ZPATH_DEFAULT);
	zLuaObj loadfile(const char* filename,EPATH p=ZPATH_DEFAULT);
	int dofile(const char* filename,EPATH p=ZPATH_DEFAULT);
	int dostring(const char* script);
	int dolibrary(const char* name);
	void setGlobalNumber(const char * name, double val);
	void setGlobalInteger(const char * name, int val);
	void setGlobalString(const char * name, const char * val);
public:
	int report(int status);
public:
	ZLuaEnvDelegate* m_delegate;
	lua_State* m_L;
	void* m_lj_alloc;
	int m_ownsL;
public:
};
ZUTIL_API int zlua_Name2EPATH(const char* p);
ZUTIL_API void zlua_regzluaenvapi(ZLuaEnv& le);
class ZUTIL_API ZLuaEnvDelegate
{
public:
	virtual const zString& GetPath(ZLuaEnv::EPATH p=ZLuaEnv::ZPATH_DEFAULT);
	virtual int OnCreateLuaEnv(ZLuaEnv& le);
	virtual int OnCloseLuaEnv(ZLuaEnv& le){return 0;};
	virtual int OnErrReport(ZLuaEnv& le,int state){return 0;};
};
#ifdef  __IPHONE_OS_VERSION_MAX_ALLOWED
class ZLuaEnvDelegateIOS : public ZLuaEnvDelegate
{
public:
	zString m_path[ZLuaEnv::ZPATH_MAXNUM];

public:
	ZLuaEnvDelegateIOS();
	virtual const zString& GetPath(ZLuaEnv::EPATH p);
	virtual int OnCreateLuaEnv(ZLuaEnv& le);
};
#endif

#ifdef WIN32
class ZLuaEnvDelegateWin32: public ZLuaEnvDelegate
{
public:
	zString m_path[ZLuaEnv::ZPATH_MAXNUM];

public:
	ZLuaEnvDelegateWin32();
	virtual const zString& GetPath(ZLuaEnv::EPATH p);
	virtual int OnCreateLuaEnv(ZLuaEnv& le);
};
#endif

#if  ZUTIL_OSTYPE==ZUTIL_ANDROID
class ZLuaEnvDelegateAndroid: public ZLuaEnvDelegate
{
public:
	zString m_path[ZLuaEnv::ZPATH_MAXNUM];

public:
	ZLuaEnvDelegateAndroid();
	virtual const zString& GetPath(ZLuaEnv::EPATH p);
	virtual int OnCreateLuaEnv(ZLuaEnv& le);
};
#endif

class ZLuaEnvDelegateTerminal: public ZLuaEnvDelegate
{
public:
	zString m_path;

public:
	ZLuaEnvDelegateTerminal(const char* path);
	virtual const zString& GetPath(ZLuaEnv::EPATH p);
	virtual int OnCreateLuaEnv(ZLuaEnv& le);
};

#ifdef  __IPHONE_OS_VERSION_MAX_ALLOWED
	#define LUA_DELEGATE  ZLuaEnvDelegateIOS
#endif

#ifdef WIN32
	#define LUA_DELEGATE  ZLuaEnvDelegateWin32
#endif

#if  ZUTIL_OSTYPE==ZUTIL_ANDROID
	#define LUA_DELEGATE  ZLuaEnvDelegateAndroid
#endif

#if !defined(LUA_DELEGATE)
	#define LUA_DELEGATE  ZLuaEnvDelegate
#endif

ZUTIL_API void zlua_createmeta(struct lua_State* L, const char* name, const struct luaL_Reg* apis);
ZUTIL_API void* zlua_asserttype(struct lua_State* L, int idx, const char* name);
struct zlua_UDProxy
{
	int flags;
	void * data;
	bool own;
};

ZUTIL_API void zlua_newtype(lua_State * L, const char * name, int flags, void * data, bool own);
ZUTIL_API void* zlua_asserttype_data(struct lua_State* L, const char* name,int flag);
ZUTIL_API void* zlua_get_type_data(struct lua_State* L, int idx, const char* name,int flag);
ZUTIL_API void* zlua_clear_type_data(struct lua_State* L, int idx, const char* name,int flag);
ZUTIL_API int zlua_traceback(struct lua_State *L) ;
ZUTIL_API int zlua_docall(lua_State *L, int narg, int mret);
#if 0
ZUTIL_API int zlua_resume(struct lua_State *L,lua_State *co, int narg);
#endif
#define ZLUA_TYPEDEF(T) (#T "_meta")
#define zlua_asserttypeT(L,T) ((T*)zlua_asserttype(L,ZLUA_TYPEDEF(T)))
#define zlua_pushstring(L,S) lua_pushlstring(L,(S),(S).length())

class ZUTIL_API ZLuaThrRun
{
	ZLuaEnv& m_le;
	zLuaObj m_firetick;
	zLuaObj m_fireevent;
	zLuaObj m_newthread;
public:
	ZLuaThrRun(ZLuaEnv& le);
	int fireTick();
	int fireEvent(const char* name,const char* arg=NULL);
	int newThreadFile(const char* name,const char* filename);
};
#endif
